package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.Constants;
import com.zzyl.common.core.domain.entity.SysUser;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.framework.interceptor.MyMetaObjectHandler;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.*;
import com.zzyl.nursing.dto.AlertDataDto;
import com.zzyl.nursing.mapper.AlertDataMapper;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserMapper;
import com.zzyl.system.mapper.SysUserRoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 报警数据Service业务层处理
 *
 * @author JBY
 * @date 2025-08-26
 */
@Service
public class AlertDataServiceImpl extends ServiceImpl<AlertDataMapper, AlertData> implements IAlertDataService {
    @Autowired
    private AlertDataMapper alertDataMapper;

    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private WebSocketServer webSocketServer;
    @Autowired
    private MyMetaObjectHandler myMetaObjectHandler;
    @Autowired
    private SysUserMapper sysUserMapper;

    /**
     * 查询报警数据
     *
     * @param id 报警数据主键
     * @return 报警数据
     */
    @Override
    public AlertData selectAlertDataById(Long id) {
        return alertDataMapper.selectById(id);
    }

    /**
     * 查询报警数据列表
     *
     * @param alertData 报警数据
     * @return 报警数据
     */
    @Override
    public List<AlertData> selectAlertDataList(AlertData alertData) {
        //获取登陆人信息
        Long loginUserId = myMetaObjectHandler.getLoginUserId();
        if (ObjectUtil.isEmpty(loginUserId)){
            throw new BaseException("当前未登录");
        }
        //查询当前登陆人对应的处理信息
        alertData.setUserId(loginUserId);
        return superAdminsAreResponsibleForAlarmData(alertData);
    }

    /**
     * 所有报警数据返回
     * @param alertData
     */
    private List<AlertData> superAdminsAreResponsibleForAlarmData(AlertData alertData) {
        List<AlertData> alertDataVo = alertDataMapper.selectAlertDataList(alertData);
        if (CollUtil.isEmpty(alertDataVo)){
            return alertDataVo;
        }
        //获取设备id
        alertDataVo.forEach(device -> {
            LambdaQueryWrapper<Device> select = new LambdaQueryWrapper<Device>()
                    .eq(Device::getIotId, device.getIotId())
                    .select(Device::getIotId)
                    .select(Device::getRemark);
            Device device1 = deviceMapper.selectOne(select);
            device.setNickname(device1.getRemark());
        });
        return alertDataVo;
    }


    /**
     * 新增报警数据
     *
     * @param alertData 报警数据
     * @return 结果
     */
    @Override
    public int insertAlertData(AlertData alertData) {
        return alertDataMapper.insert(alertData);
    }

    /**
     * 修改报警数据
     *
     * @param alertData 报警数据
     * @return 结果
     */
    @Override
    public int updateAlertData(AlertDataDto alertDataDto) {
        //参数校验
        Long loginUserId = parameterVerification(alertDataDto);
        AlertData alertDataOne = alertDataMapper.selectById(alertDataDto.getId());
        if (ObjectUtil.isEmpty(alertDataOne)){
            throw new BaseException("无可处理数据");
        }
        if (ObjectUtil.isEmpty(alertDataOne.getStatus())){
            throw new BaseException("当前数据已处理");
        }
        //查询处理人名称
        SysUser sysUser = sysUserMapper.selectUserById(loginUserId);
        if (ObjectUtil.isEmpty(sysUser)){
            throw new BaseException("当前登录用户不存在");
        }
        //进行处理报警数据
        return processAlarmData(alertDataDto,alertDataOne,sysUser);
    }

    /**
     * 报警数据参数校验
     * @param alertDataDto 报警处理请求参数
     * @return 登录用户id
     */
    private Long parameterVerification(AlertDataDto alertDataDto) {
        if (ObjectUtil.isEmpty(alertDataDto)){
            throw new BaseException("请正确填写完整处理结果表单");
        }
        if (ObjectUtil.isEmpty(alertDataDto.getId())){
            throw new BaseException("当前数据异常,请联系管理员");
        }
        Long loginUserId = myMetaObjectHandler.getLoginUserId();
        if (ObjectUtil.isEmpty(loginUserId)){
            throw new BaseException("当前未登录");
        }
        return loginUserId;
    }

    /**
     * 处理报警数据
     * @param alertDataDto 报警数据
     * @param alertDataOne 报警数据
     * @param sysUser 当前登录用户
     * @return 处理结果
     */
    private int processAlarmData(AlertDataDto alertDataDto,AlertData alertDataOne, SysUser sysUser) {
        //时间转化
        LambdaUpdateWrapper<AlertData> updateWrapper = new LambdaUpdateWrapper<AlertData>()
                .eq(AlertData::getCreateTime, alertDataOne.getCreateTime())
                .eq(AlertData::getIotId, alertDataOne.getIotId())
                .set(AlertData::getUpdateBy, sysUser.getUserId().toString())
                .set(AlertData::getProcessingTime, alertDataDto.getProcessingTime())
                .set(AlertData::getProcessingResult, alertDataDto.getProcessingResult())
                .set(AlertData::getUpdateTime,LocalDateTime.now())
                .set(AlertData::getProcessorId,sysUser.getUserId())
                .set(AlertData::getProcessorName,sysUser.getNickName())
                .set(AlertData::getStatus, 1);
        return alertDataMapper.update(null, updateWrapper);
    }

    /**
     * 批量删除报警数据
     *
     * @param ids 需要删除的报警数据主键
     * @return 结果
     */
    @Override
    public int deleteAlertDataByIds(Long[] ids) {
        return alertDataMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除报警数据信息
     *
     * @param id 报警数据主键
     * @return 结果
     */
    @Override
    public int deleteAlertDataById(Long id) {
        return alertDataMapper.deleteById(id);
    }


    /**
     * 自动过滤报警规则定时任务
     *
     * @param
     * @return
     */
    @Override
    public void alertRuleFilter() {
        // 1. 查询数据库中所有报警规则
        Long count = alertRuleMapper.selectCount(new LambdaQueryWrapper<AlertRule>()
                .eq(AlertRule::getStatus, 1));
        // 2. 如果是空 直接结束
        if (ObjUtil.isEmpty(count)) {
            return;
        }
        // 3.从Redis缓存中获取数据, 遍历封装
        List<List<DeviceData>> values = redisTemplate.opsForHash().values(Constants.CACHE_DEVICE_LAST_DATA);
        if (ArrayUtil.isEmpty(values)) {
            // 如果Redis中设备数据缓存为空 直接返回
            return;
        }
        List<DeviceData> list = new ArrayList<>();
        values.forEach(list::addAll);
        // 4. 处理数据
        //TODO 数据转换异常, 无法转化成DeviceData
        list.forEach(data -> dataAlertFilert(data));
    }

    private void dataAlertFilert(DeviceData data) {
        // 1. 如果当前上报的数据超过了一分钟, 是无效数据 直接返回
        if (data.getAlarmTime().plusMonths(1).isBefore(LocalDateTime.now())) {
            return;
        }
        // 2. 获取产品下设备对应的物模型规则
        List<AlertRule> alertRules = alertRuleMapper.selectList(new LambdaQueryWrapper<AlertRule>()
                .eq(AlertRule::getIotId, data.getIotId())
                .eq(AlertRule::getFunctionId, data.getFunctionId())
                .eq(AlertRule::getProductKey, data.getProductKey()));
        // 3. 判断规则是不是空 是空直接结束
        if (ArrayUtil.isEmpty(alertRules)) {
            return;
        }
        // 4. 遍历每一条规则 判断数据是否匹配规则
        alertRules.forEach(rule -> dataIsMatchRule(rule, data));
    }

    /**
     * 判断数据是否匹配规则
     *
     * @param rule
     * @param data
     * @return void
     */
    private void dataIsMatchRule(AlertRule rule, DeviceData data) {
        String durationKey = Constants.ALERT_TRIGGER_COUNT_PREFIX + data.getProductKey() + "_" + data.getIotId() + "_" + data.getFunctionId() + "_" + rule.getId();
        String silentKey = Constants.ALERT_SILENT_PREFIX + data.getProductKey() + "_" + data.getIotId() + "_" + data.getFunctionId() + "_" + rule.getId();

        // 1. 判断报警时间是否在范围内
        if (!dataTimeBeyondRange(rule, data)) {
            //如果超出范围直接结束
            return;
        }
        ;
        // 2. 校验数据是否达到阈值 未达到就删除报警数(持续周期) 直接结束
        if (!dataValueIsMatch(rule, data)) {
            //缓存中也要删除报警数(持续周期)
            redisTemplate.opsForHash().delete(durationKey, data.getIotId());
            return;
        }
        // 3. 判断是否在沉默周期中, 是就结束
        String silenceCycle = (String) redisTemplate.opsForValue().get(silentKey);
        if (StrUtil.isNotEmpty(silenceCycle)) {
            return;
        }
        // 4. 查询Redis中的报警数, 没有设置为1 有就+1
        Integer count = (Integer) redisTemplate.opsForValue().get(durationKey);
        if (ObjUtil.isNull(count)) {
            count = 1;
        } else {
            count++;
        }
        // 5. 判断报警数是否达到报警值
        if (ObjUtil.notEqual(count, rule.getAlertSilentPeriod())) {
            //未达到报警值, 在缓存中设置报警数(持续周期)
            redisTemplate.opsForValue().set(durationKey, count);
            return;
        }
        ;
        // 6. Redis缓存中删除报警数, 增加沉默周期
        redisTemplate.delete(durationKey);
        redisTemplate.opsForValue().set(silentKey, "1", rule.getAlertSilentPeriod(), TimeUnit.MINUTES);

        // 7. 批量保存报警数值
        batchInsertAlertData(rule, data);
    }

    private void batchInsertAlertData(AlertRule rule, DeviceData data) {
        // 查询报警数据要通知的用户列表
        List<Long> adminIds = sysUserRoleMapper.selectUserIdByRoleName("超级管理员");
        List<Long> otherUserIds = null;
        if (rule.getAlertDataType() == 0) {
            if (data.getLocationType() == 0) {
                //老人
                otherUserIds = deviceMapper.selectDeviceByIotIdWithElder(data.getIotId());
            } else {
                //床位
                otherUserIds = deviceMapper.selectNursingIdsByIotIdWithBed(data.getIotId());
            }
        } else {
            otherUserIds = sysUserRoleMapper.selectUserIdByRoleName("维修工");
        }
        Collection<Long> userIds = CollUtil.addAll(adminIds, otherUserIds);
        userIds.remove(null);

        List<AlertData> alertDataList = new ArrayList<>();
        userIds.forEach(userId -> {
            AlertData alertData = BeanUtil.toBean(data, AlertData.class);
            alertData.setId(null);
            alertData.setAlertRuleId(rule.getId());
            String reason = StringUtils.format("报警原因:{}{}{}, 持续{}个周期", rule.getFunctionName(), rule.getOperator(), rule.getValue(), rule.getDuration());
            alertData.setAlertReason(reason);
            alertData.setType(rule.getAlertDataType());
            alertData.setStatus(0);
            alertData.setUserId(userId);
            alertDataList.add(alertData);
        });
        this.saveBatch(alertDataList);
        //通过websocket通知用户
        webSocketNotify(alertDataList.get(0), rule, userIds);
    }

    /**
     * websocket通知用户
     *
     * @param alertData
     * @param rule
     * @param userIds
     */
    private void webSocketNotify(AlertData alertData, AlertRule rule, Collection<Long> userIds) {
        AlertNotifyVo alertNotifyVo = BeanUtil.toBean(alertData, AlertNotifyVo.class);
        alertNotifyVo.setFunctionName(rule.getFunctionName());
        alertNotifyVo.setAlertDataType(rule.getAlertDataType());
        alertNotifyVo.setVoiceNotifyStatus(0);
        alertNotifyVo.setNotifyType(1);
        if (alertData.getLocationType() == 1 && alertData.getPhysicalLocationType() == 2) {
            alertNotifyVo.setIsAllConsumer(true);
        } else {
            alertNotifyVo.setIsAllConsumer(false);
        }
        webSocketServer.sendMessageToConsumer(alertNotifyVo, userIds);
    }

    //检查上报的数据是否在报警阈值内
    private boolean dataValueIsMatch(AlertRule rule, DeviceData data) {
        try {
            Object dataValue = data.getDataValue();
            Double value = Double.valueOf(dataValue.toString());
            Double ruleValue = rule.getValue();
            //0相等 1左大 -1右大
            if (">=".equals(rule.getOperator()) && Double.compare(value, ruleValue) < 0
                    || "<".equals(rule.getOperator()) && Double.compare(value, ruleValue) >= 0) {
                return false;
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 判断数据时间是否在报警时间范围内
     *
     * @param rule
     * @param data
     * @return boolean
     */
    private boolean dataTimeBeyondRange(AlertRule rule, DeviceData data) {
        String[] split = rule.getAlertEffectivePeriod().split("~");
        LocalTime start = LocalTime.parse(split[0]);
        LocalTime end = LocalTime.parse(split[1]);
        LocalTime alertTime = data.getAlarmTime().toLocalTime();
        if (alertTime.isBefore(start) || alertTime.isAfter(end)) {
            return false;
        }
        return true;
    }
}
